Load Libraries


packages <- c(
  'ggplot2','tidyverse','plotly','leaflet',
  'shiny','shinyWidgets','shinydashboard',
  'xts','forecast','TTR',
  'DT','lubridate','RColorBrewer','scales','stopwords',
  'tidytext','stringr','wordcloud','wordcloud2','scales','dplyr','rfm',
  'SnowballC','textmineR','topicmodels','textclean','tm'
)
for (package in packages) { 
  if (!require(package, character.only = T, quietly = T)) {
    install.packages(package)
    library(package, character.only = T)
  }
}

Load data

crm <- read_csv("CRM_interacions_table.csv")
gift <- read_csv("gift_transactions_table.csv")
video <- read_csv("video_email_data_table.csv")
constituent <- read_csv("constituent_profiles_table.csv")

Part 1: The Untapped Potential: Understanding Our Donor Landscape

CRM Data Overview

# CRM Interaction Type
g <- crm %>%
        group_by(CRM_INTERACTION_TYPE) %>%
        summarise(Total = n()) %>%
        select(CRM_INTERACTION_TYPE, Total) %>%
        ggplot(aes(x = reorder(CRM_INTERACTION_TYPE,Total) ,y = Total))  +
        geom_bar(stat = "identity",width = 0.5, fill='black')  +
        scale_y_continuous(labels = scales::comma) +
        labs(x ="CRM Interaction Type", y = "Count") + coord_flip() +
        theme(legend.text = element_text(size = 12),
              legend.title = element_text(size = 12),
              axis.title = element_text(size = 14),
              axis.text = element_text(size = 12))
      
ggplotly(g)

CRM Interaction Over Time

crm <- crm %>%
        mutate(Year = lubridate::year(CRM_INTERACTION_DATE),
               Quarter = lubridate::quarter(CRM_INTERACTION_DATE),
               Month = lubridate::month(CRM_INTERACTION_DATE, label = TRUE),
               DOW = lubridate::wday(CRM_INTERACTION_DATE, label=TRUE))

CRM Interaction by Year

crm_year <- crm %>%
group_by(Year, CRM_INTERACTION_TYPE) %>%
        summarise(Total = n()) %>%
        select(Year,CRM_INTERACTION_TYPE, Total)

   g <- ggplot(crm_year, aes(as.factor(Year), Total, group=CRM_INTERACTION_TYPE, colour = CRM_INTERACTION_TYPE)) + 
      geom_line( linewidth=1) + theme_minimal() +
      labs(x = "Year", y = "Total", color="CRM Interaction Type") + 
      scale_y_continuous(labels = comma) +
      theme(legend.text = element_text(size = 10),
            legend.title = element_text(size = 10),
            axis.title = element_text(size = 10),
            axis.text = element_text(size = 10),
            axis.text.x = element_text(angle = 0, hjust = 1))
ggplotly(g)

CRM Interaction by Quarter

crm %>%
group_by(Quarter, CRM_INTERACTION_TYPE) %>%
        summarise(Total = n()) %>%
        select(Quarter,CRM_INTERACTION_TYPE, Total) %>% 
      ggplot(aes(as.factor(Quarter), Total, group=CRM_INTERACTION_TYPE, colour = CRM_INTERACTION_TYPE)) + 
      geom_line( linewidth=1) + theme_minimal() +
      labs(x = "Quarter", y = "Total", color="CRM Interaction Type") + 
      scale_y_continuous(labels = comma) +
      theme(legend.text = element_text(size = 10),
            legend.title = element_text(size = 10),
            axis.title = element_text(size = 10),
            axis.text = element_text(size = 10),
            axis.text.x = element_text(angle = 0, hjust = 1))

CRM Interaction by Month

crm %>%
group_by(Month, CRM_INTERACTION_TYPE) %>%
        summarise(Total = n()) %>%
        select(Month,CRM_INTERACTION_TYPE, Total) %>% 
      ggplot(aes(as.factor(Month), Total, group=CRM_INTERACTION_TYPE, colour = CRM_INTERACTION_TYPE)) + 
      geom_line( linewidth=1) + theme_minimal() +
      labs(x = "Quarter", y = "Total", color="CRM Interaction Type") + 
      scale_y_continuous(labels = comma) +
      theme(legend.text = element_text(size = 10),
            legend.title = element_text(size = 10),
            axis.title = element_text(size = 10),
            axis.text = element_text(size = 10),
            axis.text.x = element_text(angle = 0, hjust = 1))

CRM Interaction by Day of Week

crm %>%
group_by(DOW, CRM_INTERACTION_TYPE) %>%
        summarise(Total = n()) %>%
        select(DOW,CRM_INTERACTION_TYPE, Total) %>% 
      ggplot(aes(as.factor(DOW), Total, group=CRM_INTERACTION_TYPE, colour = CRM_INTERACTION_TYPE)) + 
      geom_line( linewidth=1) + theme_minimal() +
      labs(x = "Day of Week", y = "Total", color="CRM Interaction Type") + 
      scale_y_continuous(labels = comma) +
      theme(legend.text = element_text(size = 10),
            legend.title = element_text(size = 10),
            axis.title = element_text(size = 10),
            axis.text = element_text(size = 10),
            axis.text.x = element_text(angle = 0, hjust = 1))

Gift Overview

Gifts by CRM Interaction Type

left_join(gift,crm,by='CONSTITUENT_ID') %>%
  group_by(CRM_INTERACTION_TYPE) %>%
  summarise(Total = sum(AMOUNT)) %>%
  select(CRM_INTERACTION_TYPE,Total) %>%
  ggplot(aes(x = reorder(CRM_INTERACTION_TYPE,Total) ,y = Total))  +
        geom_bar(stat = "identity",width = 0.5, fill='black')  +
        scale_y_continuous(labels = scales::comma) +
        labs(x ="CRM Interaction Type", y = "Donations") + coord_flip() +
        theme(legend.text = element_text(size = 12),
              legend.title = element_text(size = 12),
              axis.title = element_text(size = 14),
              axis.text = element_text(size = 12))

Gifts overtime

gift <- gift %>%
        mutate(Year = lubridate::year(GIFT_DATE),
               Quarter = lubridate::quarter(GIFT_DATE),
               Month = lubridate::month(GIFT_DATE, label = TRUE),
               DOW = lubridate::wday(GIFT_DATE, label=TRUE))

Gift by Year

g <- gift %>%
group_by(Year) %>%
        summarise(Total = sum(AMOUNT)) %>%
        select(Year, Total) %>% 
        na.omit() %>%
      ggplot(aes(Year, Total)) + 
      geom_bar(stat = "identity",width = 0.5, fill='black') + theme_minimal() +
      labs(x = "Year", y = "Total") + 
      scale_y_continuous(labels = comma) +
      theme(legend.text = element_text(size = 10),
            legend.title = element_text(size = 10),
            axis.title = element_text(size = 10),
            axis.text = element_text(size = 10),
            axis.text.x = element_text(angle = 90, hjust = 1))
ggplotly(g)

Gift by Quarter

g <- gift %>%
group_by(Quarter) %>%
        summarise(Total = sum(AMOUNT)) %>%
        select(Quarter, Total) %>% 
        na.omit() %>%
      ggplot(aes(Quarter, Total)) + 
      geom_bar(stat = "identity",width = 0.5, fill='black') + theme_minimal() +
      labs(x = "Quarter", y = "Total") + 
      scale_y_continuous(labels = comma) +
      theme(legend.text = element_text(size = 10),
            legend.title = element_text(size = 10),
            axis.title = element_text(size = 10),
            axis.text = element_text(size = 10),
            axis.text.x = element_text(angle = 0, hjust = 1))
ggplotly(g)

Gift by Month

g <- gift %>%
group_by(Month) %>%
        summarise(Total = sum(AMOUNT)) %>%
        select(Month, Total) %>% 
        na.omit() %>%
      ggplot(aes(Month, Total)) + 
      geom_bar(stat = "identity",width = 0.5, fill='black') + theme_minimal() +
      labs(x = "Month", y = "Total") + 
      scale_y_continuous(labels = comma) +
      theme(legend.text = element_text(size = 10),
            legend.title = element_text(size = 10),
            axis.title = element_text(size = 10),
            axis.text = element_text(size = 10),
            axis.text.x = element_text(angle = 0, hjust = 1))
ggplotly(g)

Gift by Day of Week

g <- gift %>%
group_by(DOW) %>%
        summarise(Total = sum(AMOUNT)) %>%
        select(DOW, Total) %>% 
        na.omit() %>%
      ggplot(aes(DOW, Total)) + 
      geom_bar(stat = "identity",width = 0.5, fill='black') + theme_minimal() +
      labs(x = "Day of Week", y = "Total") + 
      scale_y_continuous(labels = comma) +
      theme(legend.text = element_text(size = 10),
            legend.title = element_text(size = 10),
            axis.title = element_text(size = 10),
            axis.text = element_text(size = 10),
            axis.text.x = element_text(angle = 0, hjust = 1))
ggplotly(g)

Video Overview

Video Views over time

video <- video %>%
        mutate(Year = lubridate::year(SENT_DATE),
               Quarter = lubridate::quarter(SENT_DATE),
               Month = lubridate::month(SENT_DATE, label = TRUE),
               DOW = lubridate::wday(SENT_DATE, label=TRUE))

Video views by year

g <- video %>%
group_by(Year) %>%
        summarise(Total = sum(VIDEO_VIEWS)) %>%
        select(Year, Total) %>% 
        na.omit() %>%
      ggplot(aes(as.factor(Year), Total)) + 
      geom_bar(stat = "identity",width = 0.5, fill='black') + theme_minimal() +
      labs(x = "Year", y = "Total") + 
      scale_y_continuous(labels = comma) +
      theme(legend.text = element_text(size = 10),
            legend.title = element_text(size = 10),
            axis.title = element_text(size = 10),
            axis.text = element_text(size = 10),
            axis.text.x = element_text(angle = 0, hjust = 1))
ggplotly(g)

Video Views by Quarter

g <- video %>%
group_by(Quarter) %>%
        summarise(Total = sum(VIDEO_VIEWS)) %>%
        select(Quarter, Total) %>% 
        na.omit() %>%
      ggplot(aes(Quarter, Total)) + 
      geom_bar(stat = "identity",width = 0.5, fill='black') + theme_minimal() +
      labs(x = "Quarter", y = "Total") + 
      scale_y_continuous(labels = comma) +
      theme(legend.text = element_text(size = 10),
            legend.title = element_text(size = 10),
            axis.title = element_text(size = 10),
            axis.text = element_text(size = 10),
            axis.text.x = element_text(angle = 0, hjust = 1))
ggplotly(g)

Video Views by Month

g <- video %>%
group_by(Month) %>%
        summarise(Total = sum(VIDEO_VIEWS)) %>%
        select(Month, Total) %>% 
        na.omit() %>%
      ggplot(aes(Month, Total)) + 
      geom_bar(stat = "identity",width = 0.5, fill='black') + theme_minimal() +
      labs(x = "Month", y = "Total") + 
      scale_y_continuous(labels = comma) +
      theme(legend.text = element_text(size = 10),
            legend.title = element_text(size = 10),
            axis.title = element_text(size = 10),
            axis.text = element_text(size = 10),
            axis.text.x = element_text(angle = 0, hjust = 1))
ggplotly(g)

Video Views by Day of Week

g <- video %>%
group_by(DOW) %>%
        summarise(Total = sum(VIDEO_VIEWS)) %>%
        select(DOW, Total) %>% 
        na.omit() %>%
      ggplot(aes(DOW, Total)) + 
      geom_bar(stat = "identity",width = 0.5, fill='black') + theme_minimal() +
      labs(x = "Day of the week", y = "Total") + 
      scale_y_continuous(labels = comma) +
      theme(legend.text = element_text(size = 10),
            legend.title = element_text(size = 10),
            axis.title = element_text(size = 10),
            axis.text = element_text(size = 10),
            axis.text.x = element_text(angle = 0, hjust = 1))
ggplotly(g)

Part 2: Customer Segmentation

Donor RFM Analysis


rfm_df <- gift %>%
  select(CONSTITUENT_ID,GIFT_DATE,AMOUNT) %>%
  na.omit()

names(rfm_df)[names(rfm_df) == 'CONSTITUENT_ID'] <- 'customer_id'

#rfm model setup
analysis_date <- lubridate::as_date(today(), tz = "UTC")

report <- rfm_table_order(rfm_df, customer_id,GIFT_DATE,AMOUNT, analysis_date)
#segment
segment_titles <- c("First Grade", "Loyal", "Likely to be Loyal",
                    "New Ones", "Could be Promising", "Require Assistance", "Getting Less Frequent",
                    "Almost Out", "Can't Lose Them", "Don’t Show Up at All")
#numerical thresholds
 r_low <- c(4, 2, 3, 4, 3, 2, 2, 1, 1, 1)
 r_high <- c(5, 5, 5, 5, 4, 3, 3, 2, 1, 2)
 f_low <- c(4, 3, 1, 1, 1, 2, 1, 2, 4, 1)
 f_high <- c(5, 5, 3, 1, 1, 3, 2, 5, 5, 2)
 m_low <- c(4, 3, 1, 1, 1, 2, 1, 2, 4, 1)
 m_high  <- c(5, 5, 3, 1, 1, 3, 2, 5, 5, 2)

divisions<-rfm_segment(report, segment_titles, r_low, r_high, f_low, f_high, m_low, m_high)

division_count <- divisions %>% count(segment) %>% arrange(desc(n)) %>% rename(Segment = segment, Count = n)

RFM heatmap

rfm_plot_heatmap(report)

rfm_plot_bar_chart(report)

rfm_plot_histogram(report)

Full Constituent Base Segmentation

`segment groups` <- c(unique(division_count$Segment))
ggplot(division_count, aes(area = Count, fill = `segment groups`, label = division_count$Segment)) +
  geom_treemap()
Error in ggplot(division_count, aes(area = Count, fill = `segment groups`,  : 
  could not find function "ggplot"

Donor Lifetime Value

# Calculate average donation per customer
avg_donation_per_customer <- gift %>%
  group_by(CONSTITUENT_ID) %>%
  summarize(avg_revenue = mean(AMOUNT))

# Calculate average donor lifespan (simplified, using the number of months)
avg_donor_lifespan <- gift %>%
  group_by(CONSTITUENT_ID) %>% 
  summarize(avg_lifespan = as.numeric(difftime(max(GIFT_DATE), min(GIFT_DATE), units = "days"))) %>%
  na.omit()

# Calculate CLV
clv_df <- inner_join(avg_donation_per_customer,avg_donor_lifespan,by='CONSTITUENT_ID') %>%
group_by(CONSTITUENT_ID) %>%
  mutate(CLV_calc = avg_revenue * avg_lifespan) %>%
  select(CONSTITUENT_ID,CLV_calc)
#clv <- avg_revenue_per_customer$avg_revenue * avg_lifespan$avg_lifespan

#print(clv)

Part 3: The Path Forward: Activating Our Strategy

Top donor prioritization matrix

Donation Forecasting

Predictive Modeling for Next-Best Gift

LS0tCnRpdGxlOiAiQXByYSBEYXRhIFNjaWVuY2UgQ2hhbGxlbmdlIDIwMjUiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCiMgTG9hZCBMaWJyYXJpZXMKYGBge3J9CgpwYWNrYWdlcyA8LSBjKAogICdnZ3Bsb3QyJywndGlkeXZlcnNlJywncGxvdGx5JywnbGVhZmxldCcsCiAgJ3NoaW55Jywnc2hpbnlXaWRnZXRzJywnc2hpbnlkYXNoYm9hcmQnLAogICd4dHMnLCdmb3JlY2FzdCcsJ1RUUicsJ3RyZWVtYXBpZnknLAogICdEVCcsJ2x1YnJpZGF0ZScsJ1JDb2xvckJyZXdlcicsJ3NjYWxlcycsJ3N0b3B3b3JkcycsCiAgJ3RpZHl0ZXh0Jywnc3RyaW5ncicsJ3dvcmRjbG91ZCcsJ3dvcmRjbG91ZDInLCdzY2FsZXMnLCdkcGx5cicsJ3JmbScsCiAgJ1Nub3diYWxsQycsJ3RleHRtaW5lUicsJ3RvcGljbW9kZWxzJywndGV4dGNsZWFuJywndG0nCikKZm9yIChwYWNrYWdlIGluIHBhY2thZ2VzKSB7IAogIGlmICghcmVxdWlyZShwYWNrYWdlLCBjaGFyYWN0ZXIub25seSA9IFQsIHF1aWV0bHkgPSBUKSkgewogICAgaW5zdGFsbC5wYWNrYWdlcyhwYWNrYWdlKQogICAgbGlicmFyeShwYWNrYWdlLCBjaGFyYWN0ZXIub25seSA9IFQpCiAgfQp9CmBgYAoKIyBMb2FkIGRhdGEKYGBge3J9CmNybSA8LSByZWFkX2NzdigiQ1JNX2ludGVyYWNpb25zX3RhYmxlLmNzdiIpCmdpZnQgPC0gcmVhZF9jc3YoImdpZnRfdHJhbnNhY3Rpb25zX3RhYmxlLmNzdiIpCnZpZGVvIDwtIHJlYWRfY3N2KCJ2aWRlb19lbWFpbF9kYXRhX3RhYmxlLmNzdiIpCmNvbnN0aXR1ZW50IDwtIHJlYWRfY3N2KCJjb25zdGl0dWVudF9wcm9maWxlc190YWJsZS5jc3YiKQpgYGAKCiMgUGFydCAxOiBUaGUgVW50YXBwZWQgUG90ZW50aWFsOiBVbmRlcnN0YW5kaW5nIE91ciBEb25vciBMYW5kc2NhcGUKCiMjIENSTSBEYXRhIE92ZXJ2aWV3CmBgYHtyfQojIENSTSBJbnRlcmFjdGlvbiBUeXBlCmcgPC0gY3JtICU+JQogICAgICAgIGdyb3VwX2J5KENSTV9JTlRFUkFDVElPTl9UWVBFKSAlPiUKICAgICAgICBzdW1tYXJpc2UoVG90YWwgPSBuKCkpICU+JQogICAgICAgIHNlbGVjdChDUk1fSU5URVJBQ1RJT05fVFlQRSwgVG90YWwpICU+JQogICAgICAgIGdncGxvdChhZXMoeCA9IHJlb3JkZXIoQ1JNX0lOVEVSQUNUSU9OX1RZUEUsVG90YWwpICx5ID0gVG90YWwpKSAgKwogICAgICAgIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLHdpZHRoID0gMC41LCBmaWxsPSdibGFjaycpICArCiAgICAgICAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHNjYWxlczo6Y29tbWEpICsKICAgICAgICBsYWJzKHggPSJDUk0gSW50ZXJhY3Rpb24gVHlwZSIsIHkgPSAiQ291bnQiKSArIGNvb3JkX2ZsaXAoKSArCiAgICAgICAgdGhlbWUobGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwKICAgICAgICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwKICAgICAgICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCksCiAgICAgICAgICAgICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMikpCiAgICAgIApnZ3Bsb3RseShnKQpgYGAKCgojIyMgQ1JNIEludGVyYWN0aW9uIE92ZXIgVGltZQpgYGB7cn0KY3JtIDwtIGNybSAlPiUKICAgICAgICBtdXRhdGUoWWVhciA9IGx1YnJpZGF0ZTo6eWVhcihDUk1fSU5URVJBQ1RJT05fREFURSksCiAgICAgICAgICAgICAgIFF1YXJ0ZXIgPSBsdWJyaWRhdGU6OnF1YXJ0ZXIoQ1JNX0lOVEVSQUNUSU9OX0RBVEUpLAogICAgICAgICAgICAgICBNb250aCA9IGx1YnJpZGF0ZTo6bW9udGgoQ1JNX0lOVEVSQUNUSU9OX0RBVEUsIGxhYmVsID0gVFJVRSksCiAgICAgICAgICAgICAgIERPVyA9IGx1YnJpZGF0ZTo6d2RheShDUk1fSU5URVJBQ1RJT05fREFURSwgbGFiZWw9VFJVRSkpCmBgYAoKIyMjIENSTSBJbnRlcmFjdGlvbiBieSBZZWFyCmBgYHtyfQpjcm1feWVhciA8LSBjcm0gJT4lCmdyb3VwX2J5KFllYXIsIENSTV9JTlRFUkFDVElPTl9UWVBFKSAlPiUKICAgICAgICBzdW1tYXJpc2UoVG90YWwgPSBuKCkpICU+JQogICAgICAgIHNlbGVjdChZZWFyLENSTV9JTlRFUkFDVElPTl9UWVBFLCBUb3RhbCkKCiAgIGcgPC0gZ2dwbG90KGNybV95ZWFyLCBhZXMoYXMuZmFjdG9yKFllYXIpLCBUb3RhbCwgZ3JvdXA9Q1JNX0lOVEVSQUNUSU9OX1RZUEUsIGNvbG91ciA9IENSTV9JTlRFUkFDVElPTl9UWVBFKSkgKyAKICAgICAgZ2VvbV9saW5lKCBsaW5ld2lkdGg9MSkgKyB0aGVtZV9taW5pbWFsKCkgKwogICAgICBsYWJzKHggPSAiWWVhciIsIHkgPSAiVG90YWwiLCBjb2xvcj0iQ1JNIEludGVyYWN0aW9uIFR5cGUiKSArIAogICAgICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICAgICAgdGhlbWUobGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICAgICAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICAgICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICAgICAgICAgICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICAgICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gMCwgaGp1c3QgPSAxKSkKZ2dwbG90bHkoZykKYGBgCgojIyMgQ1JNIEludGVyYWN0aW9uIGJ5IFF1YXJ0ZXIKYGBge3J9CmNybSAlPiUKZ3JvdXBfYnkoUXVhcnRlciwgQ1JNX0lOVEVSQUNUSU9OX1RZUEUpICU+JQogICAgICAgIHN1bW1hcmlzZShUb3RhbCA9IG4oKSkgJT4lCiAgICAgICAgc2VsZWN0KFF1YXJ0ZXIsQ1JNX0lOVEVSQUNUSU9OX1RZUEUsIFRvdGFsKSAlPiUgCiAgICAgIGdncGxvdChhZXMoYXMuZmFjdG9yKFF1YXJ0ZXIpLCBUb3RhbCwgZ3JvdXA9Q1JNX0lOVEVSQUNUSU9OX1RZUEUsIGNvbG91ciA9IENSTV9JTlRFUkFDVElPTl9UWVBFKSkgKyAKICAgICAgZ2VvbV9saW5lKCBsaW5ld2lkdGg9MSkgKyB0aGVtZV9taW5pbWFsKCkgKwogICAgICBsYWJzKHggPSAiUXVhcnRlciIsIHkgPSAiVG90YWwiLCBjb2xvcj0iQ1JNIEludGVyYWN0aW9uIFR5cGUiKSArIAogICAgICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICAgICAgdGhlbWUobGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICAgICAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICAgICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICAgICAgICAgICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICAgICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gMCwgaGp1c3QgPSAxKSkKCmBgYAoKIyMjIENSTSBJbnRlcmFjdGlvbiBieSBNb250aApgYGB7cn0KY3JtICU+JQpncm91cF9ieShNb250aCwgQ1JNX0lOVEVSQUNUSU9OX1RZUEUpICU+JQogICAgICAgIHN1bW1hcmlzZShUb3RhbCA9IG4oKSkgJT4lCiAgICAgICAgc2VsZWN0KE1vbnRoLENSTV9JTlRFUkFDVElPTl9UWVBFLCBUb3RhbCkgJT4lIAogICAgICBnZ3Bsb3QoYWVzKGFzLmZhY3RvcihNb250aCksIFRvdGFsLCBncm91cD1DUk1fSU5URVJBQ1RJT05fVFlQRSwgY29sb3VyID0gQ1JNX0lOVEVSQUNUSU9OX1RZUEUpKSArIAogICAgICBnZW9tX2xpbmUoIGxpbmV3aWR0aD0xKSArIHRoZW1lX21pbmltYWwoKSArCiAgICAgIGxhYnMoeCA9ICJNb250aCIsIHkgPSAiVG90YWwiLCBjb2xvcj0iQ1JNIEludGVyYWN0aW9uIFR5cGUiKSArIAogICAgICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICAgICAgdGhlbWUobGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICAgICAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICAgICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICAgICAgICAgICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICAgICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gMCwgaGp1c3QgPSAxKSkKYGBgCgoKIyMjIENSTSBJbnRlcmFjdGlvbiBieSBEYXkgb2YgV2VlawpgYGB7cn0KY3JtICU+JQpncm91cF9ieShET1csIENSTV9JTlRFUkFDVElPTl9UWVBFKSAlPiUKICAgICAgICBzdW1tYXJpc2UoVG90YWwgPSBuKCkpICU+JQogICAgICAgIHNlbGVjdChET1csQ1JNX0lOVEVSQUNUSU9OX1RZUEUsIFRvdGFsKSAlPiUgCiAgICAgIGdncGxvdChhZXMoYXMuZmFjdG9yKERPVyksIFRvdGFsLCBncm91cD1DUk1fSU5URVJBQ1RJT05fVFlQRSwgY29sb3VyID0gQ1JNX0lOVEVSQUNUSU9OX1RZUEUpKSArIAogICAgICBnZW9tX2xpbmUoIGxpbmV3aWR0aD0xKSArIHRoZW1lX21pbmltYWwoKSArCiAgICAgIGxhYnMoeCA9ICJEYXkgb2YgV2VlayIsIHkgPSAiVG90YWwiLCBjb2xvcj0iQ1JNIEludGVyYWN0aW9uIFR5cGUiKSArIAogICAgICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICAgICAgdGhlbWUobGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICAgICAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICAgICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICAgICAgICAgICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICAgICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gMCwgaGp1c3QgPSAxKSkKYGBgCiMjIEdpZnQgT3ZlcnZpZXcKCiMjIyBHaWZ0cyBieSBDUk0gSW50ZXJhY3Rpb24gVHlwZQpgYGB7cn0KbGVmdF9qb2luKGdpZnQsY3JtLGJ5PSdDT05TVElUVUVOVF9JRCcpICU+JQogIGdyb3VwX2J5KENSTV9JTlRFUkFDVElPTl9UWVBFKSAlPiUKICBzdW1tYXJpc2UoVG90YWwgPSBzdW0oQU1PVU5UKSkgJT4lCiAgc2VsZWN0KENSTV9JTlRFUkFDVElPTl9UWVBFLFRvdGFsKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSByZW9yZGVyKENSTV9JTlRFUkFDVElPTl9UWVBFLFRvdGFsKSAseSA9IFRvdGFsKSkgICsKICAgICAgICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5Iix3aWR0aCA9IDAuNSwgZmlsbD0nYmxhY2snKSAgKwogICAgICAgIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBzY2FsZXM6OmNvbW1hKSArCiAgICAgICAgbGFicyh4ID0iQ1JNIEludGVyYWN0aW9uIFR5cGUiLCB5ID0gIkRvbmF0aW9ucyIpICsgY29vcmRfZmxpcCgpICsKICAgICAgICB0aGVtZShsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLAogICAgICAgICAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLAogICAgICAgICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0KSwKICAgICAgICAgICAgICBheGlzLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSkKCgpgYGAKCiMjIyBHaWZ0cyBvdmVydGltZQpgYGB7cn0KZ2lmdCA8LSBnaWZ0ICU+JQogICAgICAgIG11dGF0ZShZZWFyID0gbHVicmlkYXRlOjp5ZWFyKEdJRlRfREFURSksCiAgICAgICAgICAgICAgIFF1YXJ0ZXIgPSBsdWJyaWRhdGU6OnF1YXJ0ZXIoR0lGVF9EQVRFKSwKICAgICAgICAgICAgICAgTW9udGggPSBsdWJyaWRhdGU6Om1vbnRoKEdJRlRfREFURSwgbGFiZWwgPSBUUlVFKSwKICAgICAgICAgICAgICAgRE9XID0gbHVicmlkYXRlOjp3ZGF5KEdJRlRfREFURSwgbGFiZWw9VFJVRSkpCmBgYAoKIyMjIEdpZnQgYnkgWWVhcgpgYGB7cn0KZyA8LSBnaWZ0ICU+JQpncm91cF9ieShZZWFyKSAlPiUKICAgICAgICBzdW1tYXJpc2UoVG90YWwgPSBzdW0oQU1PVU5UKSkgJT4lCiAgICAgICAgc2VsZWN0KFllYXIsIFRvdGFsKSAlPiUgCiAgICAgICAgbmEub21pdCgpICU+JQogICAgICBnZ3Bsb3QoYWVzKFllYXIsIFRvdGFsKSkgKyAKICAgICAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsd2lkdGggPSAwLjUsIGZpbGw9J2JsYWNrJykgKyB0aGVtZV9taW5pbWFsKCkgKwogICAgICBsYWJzKHggPSAiWWVhciIsIHkgPSAiVG90YWwiKSArIAogICAgICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICAgICAgdGhlbWUobGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICAgICAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICAgICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICAgICAgICAgICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICAgICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIGhqdXN0ID0gMSkpCmdncGxvdGx5KGcpCmBgYAoKIyMjIEdpZnQgYnkgUXVhcnRlcgpgYGB7cn0KZyA8LSBnaWZ0ICU+JQpncm91cF9ieShRdWFydGVyKSAlPiUKICAgICAgICBzdW1tYXJpc2UoVG90YWwgPSBzdW0oQU1PVU5UKSkgJT4lCiAgICAgICAgc2VsZWN0KFF1YXJ0ZXIsIFRvdGFsKSAlPiUgCiAgICAgICAgbmEub21pdCgpICU+JQogICAgICBnZ3Bsb3QoYWVzKFF1YXJ0ZXIsIFRvdGFsKSkgKyAKICAgICAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsd2lkdGggPSAwLjUsIGZpbGw9J2JsYWNrJykgKyB0aGVtZV9taW5pbWFsKCkgKwogICAgICBsYWJzKHggPSAiUXVhcnRlciIsIHkgPSAiVG90YWwiKSArIAogICAgICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICAgICAgdGhlbWUobGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICAgICAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICAgICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICAgICAgICAgICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICAgICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gMCwgaGp1c3QgPSAxKSkKZ2dwbG90bHkoZykKYGBgCgojIyMgR2lmdCBieSBNb250aApgYGB7cn0KZyA8LSBnaWZ0ICU+JQpncm91cF9ieShNb250aCkgJT4lCiAgICAgICAgc3VtbWFyaXNlKFRvdGFsID0gc3VtKEFNT1VOVCkpICU+JQogICAgICAgIHNlbGVjdChNb250aCwgVG90YWwpICU+JSAKICAgICAgICBuYS5vbWl0KCkgJT4lCiAgICAgIGdncGxvdChhZXMoTW9udGgsIFRvdGFsKSkgKyAKICAgICAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsd2lkdGggPSAwLjUsIGZpbGw9J2JsYWNrJykgKyB0aGVtZV9taW5pbWFsKCkgKwogICAgICBsYWJzKHggPSAiTW9udGgiLCB5ID0gIlRvdGFsIikgKyAKICAgICAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArCiAgICAgIHRoZW1lKGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICAgICAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLAogICAgICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICAgICAgICAgIGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLAogICAgICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDAsIGhqdXN0ID0gMSkpCmdncGxvdGx5KGcpCmBgYAoKR2lmdCBieSBEYXkgb2YgV2VlawpgYGB7cn0KZyA8LSBnaWZ0ICU+JQpncm91cF9ieShET1cpICU+JQogICAgICAgIHN1bW1hcmlzZShUb3RhbCA9IHN1bShBTU9VTlQpKSAlPiUKICAgICAgICBzZWxlY3QoRE9XLCBUb3RhbCkgJT4lIAogICAgICAgIG5hLm9taXQoKSAlPiUKICAgICAgZ2dwbG90KGFlcyhET1csIFRvdGFsKSkgKyAKICAgICAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsd2lkdGggPSAwLjUsIGZpbGw9J2JsYWNrJykgKyB0aGVtZV9taW5pbWFsKCkgKwogICAgICBsYWJzKHggPSAiRGF5IG9mIFdlZWsiLCB5ID0gIlRvdGFsIikgKyAKICAgICAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArCiAgICAgIHRoZW1lKGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICAgICAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLAogICAgICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICAgICAgICAgIGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLAogICAgICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDAsIGhqdXN0ID0gMSkpCmdncGxvdGx5KGcpCmBgYAoKIyMgVmlkZW8gT3ZlcnZpZXcKIyMjIFZpZGVvIFZpZXdzIG92ZXIgdGltZQpgYGB7cn0KdmlkZW8gPC0gdmlkZW8gJT4lCiAgICAgICAgbXV0YXRlKFllYXIgPSBsdWJyaWRhdGU6OnllYXIoU0VOVF9EQVRFKSwKICAgICAgICAgICAgICAgUXVhcnRlciA9IGx1YnJpZGF0ZTo6cXVhcnRlcihTRU5UX0RBVEUpLAogICAgICAgICAgICAgICBNb250aCA9IGx1YnJpZGF0ZTo6bW9udGgoU0VOVF9EQVRFLCBsYWJlbCA9IFRSVUUpLAogICAgICAgICAgICAgICBET1cgPSBsdWJyaWRhdGU6OndkYXkoU0VOVF9EQVRFLCBsYWJlbD1UUlVFKSkKYGBgCgojIyMgVmlkZW8gdmlld3MgYnkgeWVhcgpgYGB7cn0KZyA8LSB2aWRlbyAlPiUKZ3JvdXBfYnkoWWVhcikgJT4lCiAgICAgICAgc3VtbWFyaXNlKFRvdGFsID0gc3VtKFZJREVPX1ZJRVdTKSkgJT4lCiAgICAgICAgc2VsZWN0KFllYXIsIFRvdGFsKSAlPiUgCiAgICAgICAgbmEub21pdCgpICU+JQogICAgICBnZ3Bsb3QoYWVzKGFzLmZhY3RvcihZZWFyKSwgVG90YWwpKSArIAogICAgICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5Iix3aWR0aCA9IDAuNSwgZmlsbD0nYmxhY2snKSArIHRoZW1lX21pbmltYWwoKSArCiAgICAgIGxhYnMoeCA9ICJZZWFyIiwgeSA9ICJUb3RhbCIpICsgCiAgICAgIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKwogICAgICB0aGVtZShsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLAogICAgICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICAgICAgICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLAogICAgICAgICAgICBheGlzLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICAgICAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSAwLCBoanVzdCA9IDEpKQpnZ3Bsb3RseShnKQpgYGAKCiMjIyBWaWRlbyBWaWV3cyBieSBRdWFydGVyCmBgYHtyfQpnIDwtIHZpZGVvICU+JQpncm91cF9ieShRdWFydGVyKSAlPiUKICAgICAgICBzdW1tYXJpc2UoVG90YWwgPSBzdW0oVklERU9fVklFV1MpKSAlPiUKICAgICAgICBzZWxlY3QoUXVhcnRlciwgVG90YWwpICU+JSAKICAgICAgICBuYS5vbWl0KCkgJT4lCiAgICAgIGdncGxvdChhZXMoUXVhcnRlciwgVG90YWwpKSArIAogICAgICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5Iix3aWR0aCA9IDAuNSwgZmlsbD0nYmxhY2snKSArIHRoZW1lX21pbmltYWwoKSArCiAgICAgIGxhYnMoeCA9ICJRdWFydGVyIiwgeSA9ICJUb3RhbCIpICsgCiAgICAgIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKwogICAgICB0aGVtZShsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLAogICAgICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICAgICAgICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLAogICAgICAgICAgICBheGlzLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICAgICAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSAwLCBoanVzdCA9IDEpKQpnZ3Bsb3RseShnKQpgYGAKCiMjIyBWaWRlbyBWaWV3cyBieSBNb250aApgYGB7cn0KZyA8LSB2aWRlbyAlPiUKZ3JvdXBfYnkoTW9udGgpICU+JQogICAgICAgIHN1bW1hcmlzZShUb3RhbCA9IHN1bShWSURFT19WSUVXUykpICU+JQogICAgICAgIHNlbGVjdChNb250aCwgVG90YWwpICU+JSAKICAgICAgICBuYS5vbWl0KCkgJT4lCiAgICAgIGdncGxvdChhZXMoTW9udGgsIFRvdGFsKSkgKyAKICAgICAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsd2lkdGggPSAwLjUsIGZpbGw9J2JsYWNrJykgKyB0aGVtZV9taW5pbWFsKCkgKwogICAgICBsYWJzKHggPSAiTW9udGgiLCB5ID0gIlRvdGFsIikgKyAKICAgICAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArCiAgICAgIHRoZW1lKGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICAgICAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLAogICAgICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICAgICAgICAgIGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLAogICAgICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDAsIGhqdXN0ID0gMSkpCmdncGxvdGx5KGcpCmBgYAoKIyMjIFZpZGVvIFZpZXdzIGJ5IERheSBvZiBXZWVrCmBgYHtyfQpnIDwtIHZpZGVvICU+JQpncm91cF9ieShET1cpICU+JQogICAgICAgIHN1bW1hcmlzZShUb3RhbCA9IHN1bShWSURFT19WSUVXUykpICU+JQogICAgICAgIHNlbGVjdChET1csIFRvdGFsKSAlPiUgCiAgICAgICAgbmEub21pdCgpICU+JQogICAgICBnZ3Bsb3QoYWVzKERPVywgVG90YWwpKSArIAogICAgICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5Iix3aWR0aCA9IDAuNSwgZmlsbD0nYmxhY2snKSArIHRoZW1lX21pbmltYWwoKSArCiAgICAgIGxhYnMoeCA9ICJEYXkgb2YgdGhlIHdlZWsiLCB5ID0gIlRvdGFsIikgKyAKICAgICAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArCiAgICAgIHRoZW1lKGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICAgICAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLAogICAgICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICAgICAgICAgIGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApLAogICAgICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDAsIGhqdXN0ID0gMSkpCmdncGxvdGx5KGcpCmBgYAoKIyBQYXJ0IDI6IEN1c3RvbWVyIFNlZ21lbnRhdGlvbgojIyBEb25vciBSRk0gQW5hbHlzaXMKYGBge3J9CgpyZm1fZGYgPC0gZ2lmdCAlPiUKICBzZWxlY3QoQ09OU1RJVFVFTlRfSUQsR0lGVF9EQVRFLEFNT1VOVCkgJT4lCiAgbmEub21pdCgpCgpuYW1lcyhyZm1fZGYpW25hbWVzKHJmbV9kZikgPT0gJ0NPTlNUSVRVRU5UX0lEJ10gPC0gJ2N1c3RvbWVyX2lkJwoKI3JmbSBtb2RlbCBzZXR1cAphbmFseXNpc19kYXRlIDwtIGx1YnJpZGF0ZTo6YXNfZGF0ZSh0b2RheSgpLCB0eiA9ICJVVEMiKQoKcmVwb3J0IDwtIHJmbV90YWJsZV9vcmRlcihyZm1fZGYsIGN1c3RvbWVyX2lkLEdJRlRfREFURSxBTU9VTlQsIGFuYWx5c2lzX2RhdGUpCiNzZWdtZW50CnNlZ21lbnRfdGl0bGVzIDwtIGMoIkZpcnN0IEdyYWRlIiwgIkxveWFsIiwgIkxpa2VseSB0byBiZSBMb3lhbCIsCiAgICAgICAgICAgICAgICAgICAgIk5ldyBPbmVzIiwgIkNvdWxkIGJlIFByb21pc2luZyIsICJSZXF1aXJlIEFzc2lzdGFuY2UiLCAiR2V0dGluZyBMZXNzIEZyZXF1ZW50IiwKICAgICAgICAgICAgICAgICAgICAiQWxtb3N0IE91dCIsICJDYW4ndCBMb3NlIFRoZW0iLCAiRG9u4oCZdCBTaG93IFVwIGF0IEFsbCIpCiNudW1lcmljYWwgdGhyZXNob2xkcwogcl9sb3cgPC0gYyg0LCAyLCAzLCA0LCAzLCAyLCAyLCAxLCAxLCAxKQogcl9oaWdoIDwtIGMoNSwgNSwgNSwgNSwgNCwgMywgMywgMiwgMSwgMikKIGZfbG93IDwtIGMoNCwgMywgMSwgMSwgMSwgMiwgMSwgMiwgNCwgMSkKIGZfaGlnaCA8LSBjKDUsIDUsIDMsIDEsIDEsIDMsIDIsIDUsIDUsIDIpCiBtX2xvdyA8LSBjKDQsIDMsIDEsIDEsIDEsIDIsIDEsIDIsIDQsIDEpCiBtX2hpZ2ggIDwtIGMoNSwgNSwgMywgMSwgMSwgMywgMiwgNSwgNSwgMikKCmRpdmlzaW9uczwtcmZtX3NlZ21lbnQocmVwb3J0LCBzZWdtZW50X3RpdGxlcywgcl9sb3csIHJfaGlnaCwgZl9sb3csIGZfaGlnaCwgbV9sb3csIG1faGlnaCkKCiNuYW1lcyhkaXZpc2lvbnMpW25hbWVzKGRpdmlzaW9ucykgPT0gJ2N1c3RvbWVyX2lkJ10gPC0gJ0NPTlNUSVRVRU5UX0lEJwoKZGl2aXNpb25fY291bnQgPC0gZGl2aXNpb25zICU+JSBjb3VudChzZWdtZW50KSAlPiUgYXJyYW5nZShkZXNjKG4pKSAlPiUgcmVuYW1lKFNlZ21lbnQgPSBzZWdtZW50LCBDb3VudCA9IG4pCmBgYAoKIyMjIFJGTSBoZWF0bWFwCmBgYHtyfQpyZm1fcGxvdF9oZWF0bWFwKHJlcG9ydCkKYGBgCgpgYGB7cn0KcmZtX3Bsb3RfYmFyX2NoYXJ0KHJlcG9ydCkKYGBgCmBgYHtyfQpyZm1fcGxvdF9oaXN0b2dyYW0ocmVwb3J0KQpgYGAKIyMjIEZ1bGwgQ29uc3RpdHVlbnQgQmFzZSBTZWdtZW50YXRpb24KYGBge3J9CmBzZWdtZW50IGdyb3Vwc2AgPC0gYyh1bmlxdWUoZGl2aXNpb25fY291bnQkU2VnbWVudCkpCmdncGxvdChkaXZpc2lvbl9jb3VudCwgYWVzKGFyZWEgPSBDb3VudCwgZmlsbCA9IGBzZWdtZW50IGdyb3Vwc2AsIGxhYmVsID0gZGl2aXNpb25fY291bnQkU2VnbWVudCkpICsKICBnZW9tX3RyZWVtYXAoKQpgYGAKCiMjIERvbm9yIExpZmV0aW1lIFZhbHVlCmBgYHtyfQojIENhbGN1bGF0ZSBhdmVyYWdlIGRvbmF0aW9uIHBlciBjdXN0b21lcgphdmdfZG9uYXRpb25fcGVyX2N1c3RvbWVyIDwtIGdpZnQgJT4lCiAgZ3JvdXBfYnkoQ09OU1RJVFVFTlRfSUQpICU+JQogIHN1bW1hcml6ZShhdmdfcmV2ZW51ZSA9IG1lYW4oQU1PVU5UKSkKCiMgQ2FsY3VsYXRlIGF2ZXJhZ2UgZG9ub3IgbGlmZXNwYW4gKHNpbXBsaWZpZWQsIHVzaW5nIHRoZSBudW1iZXIgb2YgbW9udGhzKQphdmdfZG9ub3JfbGlmZXNwYW4gPC0gZ2lmdCAlPiUKICBncm91cF9ieShDT05TVElUVUVOVF9JRCkgJT4lIAogIHN1bW1hcml6ZShhdmdfbGlmZXNwYW4gPSBhcy5udW1lcmljKGRpZmZ0aW1lKG1heChHSUZUX0RBVEUpLCBtaW4oR0lGVF9EQVRFKSwgdW5pdHMgPSAiZGF5cyIpKSkgJT4lCiAgbmEub21pdCgpCgojIENhbGN1bGF0ZSBDTFYKY2x2X2RmIDwtIGlubmVyX2pvaW4oYXZnX2RvbmF0aW9uX3Blcl9jdXN0b21lcixhdmdfZG9ub3JfbGlmZXNwYW4sYnk9J0NPTlNUSVRVRU5UX0lEJykgJT4lCmdyb3VwX2J5KENPTlNUSVRVRU5UX0lEKSAlPiUKICBtdXRhdGUoQ0xWX2NhbGMgPSBhdmdfcmV2ZW51ZSAqIGF2Z19saWZlc3BhbikgJT4lCiAgc2VsZWN0KENPTlNUSVRVRU5UX0lELENMVl9jYWxjKQoKCmBgYAoKIyBQYXJ0IDM6IFRoZSBQYXRoIEZvcndhcmQ6IEFjdGl2YXRpbmcgT3VyIFN0cmF0ZWd5CiMjIFRvcCBkb25vciBwcmlvcml0aXphdGlvbiBtYXRyaXgKYGBge3J9CgpgYGAKCiMjIERvbmF0aW9uIEZvcmVjYXN0aW5nCmBgYHtyfQoKYGBgCgojIyBQcmVkaWN0aXZlIE1vZGVsaW5nIGZvciBOZXh0LUJlc3QgR2lmdApgYGB7cn0KCmBgYAoKCmBgYHtyfQoKYGBgCg==